/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ext_dev.h"
#include "gpio_if.h"

#define BLUE_LED_GPIO 5
#define GREEN_LED_GPIO 3
#define RED_LED_GPIO 6
#define EXT_DEV_LED_NUM 3

typedef struct {
    ExtDevLedIdDef extDevLedId;
    uint16_t extDevInterLedId;
    ExtDevLedActDef extDevLedAct;
    osTimerId_t blinkTimerHandle;
    osTimerFunc_t blinkTimerCb;
} ExtDevLedManageDef;

static void ExtDevInterLedBlinkingTimCb(void *argument);
static ExtDevCtlRst ExtDevInterSetLedOn(ExtDevLedIdDef extDevLedId);
static ExtDevCtlRst ExtDevInterSetLedOff(ExtDevLedIdDef extDevLedId);

static ExtDevLedManageDef g_extDevLedManage[EXT_DEV_LED_NUM] = {{
                                                                    .extDevLedId = EXT_DEV_LED_GREEN,
                                                                    .extDevInterLedId = GREEN_LED_GPIO,
                                                                    .extDevLedAct = EXT_DEV_LED_OFF,
                                                                    .blinkTimerHandle = NULL,
                                                                    .blinkTimerCb = ExtDevInterLedBlinkingTimCb,
                                                                },
                                                                {
                                                                    .extDevLedId = EXT_DEV_LED_BLUE,
                                                                    .extDevInterLedId = BLUE_LED_GPIO,
                                                                    .extDevLedAct = EXT_DEV_LED_OFF,
                                                                    .blinkTimerHandle = NULL,
                                                                    .blinkTimerCb = ExtDevInterLedBlinkingTimCb,
                                                                },
                                                                {
                                                                    .extDevLedId = EXT_DEV_LED_RED,
                                                                    .extDevInterLedId = RED_LED_GPIO,
                                                                    .extDevLedAct = EXT_DEV_LED_OFF,
                                                                    .blinkTimerHandle = NULL,
                                                                    .blinkTimerCb = ExtDevInterLedBlinkingTimCb,
                                                                }};

static void ExtDevInterLedBlinkingTimCb(void *argument)
{
    ExtDevLedIdDef extDevLedId = *(ExtDevLedIdDef *)argument;
    if (EXT_DEV_LED_ON == g_extDevLedManage[extDevLedId].extDevLedAct) {
        ExtDevInterSetLedOff(extDevLedId);
        g_extDevLedManage[extDevLedId].extDevLedAct = EXT_DEV_LED_OFF;
    } else {
        ExtDevInterSetLedOn(extDevLedId);
        g_extDevLedManage[extDevLedId].extDevLedAct = EXT_DEV_LED_ON;
    }
}

static ExtDevCtlRst ExtDevInterStopLedBlinkTimById(ExtDevLedIdDef extDevLedId)
{
    LOG_I("stop led%d timer.", g_extDevLedManage[extDevLedId].extDevInterLedId);
    if (NULL != g_extDevLedManage[extDevLedId].blinkTimerHandle) {
        if (osOK != osTimerStop(g_extDevLedManage[extDevLedId].blinkTimerHandle)) {
            LOG_E("led resource release failed!");
            return EXT_DEV_ERROR;
        }
        g_extDevLedManage[extDevLedId].blinkTimerHandle = NULL;
    }
    return EXT_DEV_SUCCESS;
}

static ExtDevCtlRst ExtDevInterSetLedOn(ExtDevLedIdDef extDevLedId)
{
    LOG_I("Set led%d on.", g_extDevLedManage[extDevLedId].extDevInterLedId);
    if (HDF_SUCCESS != GpioWrite(g_extDevLedManage[extDevLedId].extDevInterLedId, GPIO_VAL_HIGH)) {
        LOG_E("Set led%d on failed!", g_extDevLedManage[extDevLedId].extDevInterLedId);
        return EXT_DEV_ERROR;
    }
    g_extDevLedManage[extDevLedId].extDevLedAct = EXT_DEV_LED_ON;
    return EXT_DEV_SUCCESS;
}

static ExtDevCtlRst ExtDevInterSetLedOff(ExtDevLedIdDef extDevLedId)
{
    LOG_I("Set led%d off.", g_extDevLedManage[extDevLedId].extDevInterLedId);
    if (HDF_SUCCESS != GpioWrite(g_extDevLedManage[extDevLedId].extDevInterLedId, GPIO_VAL_LOW)) {
        LOG_E("Set led%d off failed!", g_extDevLedManage[extDevLedId].extDevInterLedId);
        return EXT_DEV_ERROR;
    }
    g_extDevLedManage[extDevLedId].extDevLedAct = EXT_DEV_LED_OFF;
    return EXT_DEV_SUCCESS;
}

ExtDevCtlRst ExtDevLedSetBlinking(ExtDevLedIdDef extDevLedId, uint32_t ms)
{
    LOG_I("Set led%d blinking, %d ms cycle.", g_extDevLedManage[extDevLedId].extDevInterLedId, ms);

    if (EXT_DEV_SUCCESS != ExtDevInterStopLedBlinkTimById(extDevLedId)) {
        LOG_E("Stop led timer failed!");
        return EXT_DEV_ERROR;
    }

    g_extDevLedManage[extDevLedId].blinkTimerHandle =
        osTimerNew(ExtDevInterLedBlinkingTimCb, osTimerPeriodic, &(g_extDevLedManage[extDevLedId].extDevLedId), NULL);
    if (NULL == g_extDevLedManage[extDevLedId].blinkTimerHandle) {
        LOG_E("Set led%d blink failed! Create timer error!", g_extDevLedManage[extDevLedId].extDevInterLedId);
        return EXT_DEV_ERROR;
    }
    g_extDevLedManage[extDevLedId].extDevLedAct = EXT_DEV_LED_OFF;
    if (osOK != osTimerStart(g_extDevLedManage[extDevLedId].blinkTimerHandle, ms / 2)) {
        LOG_E("Set led%d blink failed! Start timer error!", g_extDevLedManage[extDevLedId].extDevInterLedId);
        return EXT_DEV_ERROR;
    }
    return EXT_DEV_SUCCESS;
}

ExtDevCtlRst ExtDevLedInitById(ExtDevLedIdDef extDevLedId)
{
    LOG_W("extDevLedId : %d.", extDevLedId);
    if (HDF_SUCCESS != GpioSetDir(g_extDevLedManage[extDevLedId].extDevInterLedId, GPIO_DIR_OUT)) {
        LOG_E("Set led output failed!");
        return EXT_DEV_ERROR;
    }
    if (EXT_DEV_SUCCESS != ExtDevInterSetLedOff(extDevLedId)) {
        LOG_E("Set led off failed!");
        return EXT_DEV_ERROR;
    }
    if (EXT_DEV_SUCCESS != ExtDevInterStopLedBlinkTimById(extDevLedId)) {
        LOG_E("Stop led timer failed!");
        return EXT_DEV_ERROR;
    }
    return EXT_DEV_SUCCESS;
}

/*
 *
 *
 */
ExtDevCtlRst ExtDevLedInit(void)
{
    LOG_I("Led init start!");
    if (EXT_DEV_SUCCESS != ExtDevLedInitById(EXT_DEV_LED_BLUE)) { // Init blue led
        LOG_E("Set blue led off failed!");
        return EXT_DEV_ERROR;
    }
    if (EXT_DEV_SUCCESS != ExtDevLedInitById(EXT_DEV_LED_GREEN)) { // Init green led
        LOG_E("Set blue led off failed!");
        return EXT_DEV_ERROR;
    }
    if (EXT_DEV_SUCCESS != ExtDevLedInitById(EXT_DEV_LED_RED)) { // Init red led
        LOG_E("Set blue led off failed!");
        return EXT_DEV_ERROR;
    }
    LOG_I("Led init success!");
    return EXT_DEV_SUCCESS;
}

ExtDevCtlRst ExtDevLedSetSwitch(ExtDevLedIdDef extDevLedId, ExtDevLedActDef extDevLedAct)
{
    ExtDevCtlRst ret = EXT_DEV_ERROR;
    if (EXT_DEV_SUCCESS != ExtDevInterStopLedBlinkTimById(extDevLedId)) {
        LOG_E("Stop led timer failed!");
        return EXT_DEV_ERROR;
    }
    switch (extDevLedAct) {
        case EXT_DEV_LED_ON:
            ret = ExtDevInterSetLedOn(extDevLedId);
            break;
        case EXT_DEV_LED_OFF:
            ret = ExtDevInterSetLedOff(extDevLedId);
            break;
        default:
            LOG_E("unexpected extDevLedAct!!");
            ret = EXT_DEV_ERROR;
            break;
    }
    return ret;
}